#include "includes.h"
#include "vdec_impl.h"
#include "rk_type.h"
#include "rk_mpi.h"
#include "mpp_buffer.h"

MPP_VDEC_S g_vdec_devices[MAX_VDEC_DEV_NUM];

static pthread_mutex_t dec_mutex;
static int32_t vdec_mod_init = 0;

int TR_VDEC_Module_Init()
{
    int i, chnIdx;
    MPP_VDEC_CHANNEL_S *pstVdecChn;

    if (vdec_mod_init != 0)
        return TR_SUCCESS;

    vdec_mod_init = 1;

    pthread_mutex_init(&dec_mutex, NULL);

    memset(g_vdec_devices, 0L, sizeof(g_vdec_devices));

    for (i = 0; i < MAX_VDEC_DEV_NUM; i++)
    {
        g_vdec_devices[i].enModId = TR_ID_VDEC;
        strncpy(g_vdec_devices[i].strModName, MPP_MOD_VDEC, sizeof(g_vdec_devices[i].strModName)-1);
        g_vdec_devices[i].u32DevId = i;
        g_vdec_devices[i].s32CodecType = 0;
        g_vdec_devices[i].s32MaxChnNum = MAX_VDEC_CHN;
        g_vdec_devices[i].s32CurChnNum = 0;
        g_vdec_devices[i].pstChannels = (MPP_VDEC_CHANNEL_S *)calloc(MAX_VDEC_CHN, sizeof(MPP_VDEC_CHANNEL_S));

        for (chnIdx = 0; chnIdx < MAX_VDEC_CHN; chnIdx++)
        {
            pstVdecChn = &g_vdec_devices[i].pstChannels[chnIdx];
            pstVdecChn->bEnable = FALSE;
            pstVdecChn->s32ChnId = -1;
            pstVdecChn->ctx = NULL;
            pstVdecChn->mpi = NULL;
            pstVdecChn->frm_grp = NULL;
            pstVdecChn->packet = NULL;
            pstVdecChn->bStartRecv = FALSE;
        }

        TR_MPI_SYS_PIN_DATA_Init_I(&g_vdec_devices[i].stPinData, 0, 1);
    }

    return TR_SUCCESS;
}

int TR_VDEC_Module_DeInit()
{
    int i, chnIdx;
    MPP_VDEC_CHANNEL_S *pstVdecChn;

    if (vdec_mod_init != 1)
        return TR_SUCCESS;

    vdec_mod_init = 0;

    for (i = 0; i < MAX_VDEC_DEV_NUM; i++)
    {
        if (g_vdec_devices[i].pstChannels)
        {
            free(g_vdec_devices[i].pstChannels);
            g_vdec_devices[i].pstChannels = NULL;
        }

        TR_MPI_SYS_PIN_DATA_DeInit_I(&g_vdec_devices[i].stPinData);
    }

    memset(g_vdec_devices, 0L, sizeof(g_vdec_devices));
    pthread_mutex_destroy(&dec_mutex);

    return TR_SUCCESS;
}

static int is_valid_chn(VDEC_CHN VdChn)
{
    return (VdChn >= 0 && VdChn < MAX_VDEC_CHN) ? 1 : 0;
}

static int is_used(VDEC_CHN VdChn)
{
    return (g_vdec_devices[0].pstChannels[VdChn].bEnable) ? 1 : 0;
}

HI_S32 HI_MPI_VDEC_CreateChn(VDEC_CHN VdChn, const VDEC_CHN_ATTR_S *pstAttr)
{
    if (!is_valid_chn(VdChn))
    {
        LOG_ERR("VdChn %d, create failed, chn is not valid.\n", VdChn);
        return TR_FAILURE;
    }

    if (is_used(VdChn))
    {
        LOG_ERR("VdChn %d, create failed, chn is used.\n", VdChn);
        return TR_FAILURE;
    }

    int codec_type = MPP_VIDEO_CodingAVC;
    switch (pstAttr->enType)
    {
        case PT_H264:
        {
            codec_type = MPP_VIDEO_CodingAVC;
            break;
        }
        case PT_H265:
        {
            codec_type = MPP_VIDEO_CodingHEVC;
            break;
        }
        default:
        {
            LOG_ERR("VdChn %d, unkown codec type.\n", VdChn);
            return TR_FAILURE;
        }
    }

    int i;
    MPP_RET ret = MPP_OK;
    MpiCmd mpi_cmd = MPP_CMD_BASE;
    MppParam param = NULL;
    uint32_t need_split = 0;//0-frame,1-stream
    MppPollType timeout = 10;

    pthread_mutex_lock(&dec_mutex);

    MPP_VDEC_CHANNEL_S *pstVdecChn = &g_vdec_devices[0].pstChannels[VdChn];

    do {
        pstVdecChn->frm_grp = NULL;
        pstVdecChn->packet = NULL;

        mpp_packet_new(&pstVdecChn->packet);
        //mpp_packet_set_extra_data(pstVdecChn->packet);

        ret = mpp_create(&pstVdecChn->ctx, &pstVdecChn->mpi);
        if (MPP_OK != ret)
        {
            LOG_ERR("VdChn %d, mpp_create failed.\n", VdChn);
            break;
        }

        if (timeout)
        {
            param = &timeout;
            ret = pstVdecChn->mpi->control(pstVdecChn->ctx, MPP_SET_OUTPUT_TIMEOUT, param);
            if (MPP_OK != ret)
            {
                LOG_ERR("VdChn %d, failed to set MPP_SET_OUTPUT_TIMEOUT %d, ret=%d.\n", VdChn, timeout, ret);
                break;
            }
        }

        ret = mpp_init(pstVdecChn->ctx, MPP_CTX_DEC, codec_type);
        if (MPP_OK != ret)
        {
            LOG_ERR("VdChn %d, mpp_init failed.ret=%d\n", VdChn, ret);
            break;
        }

        //使用外部分配内存，使用AHardwareBuffer
        ret = mpp_buffer_group_get_external(&pstVdecChn->frm_grp, MPP_BUFFER_TYPE_DRM);
        if (MPP_OK != ret)
        {
            LOG_ERR("VdChn %d, get mpp buffer group failed ret %d\n", VdChn, ret);
            break;
        }

        //add buffer to group
        MppBufferInfo bufInfo;
        //bufinfo.size = hor_stride*ver_stride*2;//2bytes

        for (i = 0; i < 20; i++)
        {
            //VB_BLK vbBlock = TR_MPI_VB_GetBlock(VB_POOL Pool, uint32_t u32BlkSize);
            //uint64_t fd = TR_MPI_VB_Handle2Fd(vbBlock);
            //bufInfo.size = xxx;
            //bufInfo.fd = xxx;
            mpp_buffer_commit(&pstVdecChn->frm_grp, &bufInfo);
        }

        ret = pstVdecChn->mpi->control(pstVdecChn->ctx, MPP_DEC_SET_EXT_BUF_GROUP, pstVdecChn->frm_grp);
        if (ret)
        {
            LOG_ERR("VdChn %d, set buffer group failed ret %d\n", VdChn, ret);
            break;
        }

        pstVdecChn->s32ChnId = VdChn;
        pstVdecChn->bStartRecv = FALSE;
        
        pthread_mutex_init(&pstVdecChn->mutex, NULL);

        pthread_mutex_unlock(&dec_mutex);

        return TR_SUCCESS;

    } while(0);

    pthread_mutex_unlock(&dec_mutex);

    if (pstVdecChn->ctx)
    {
        mpp_destroy(pstVdecChn->ctx);
        pstVdecChn->ctx = NULL;
        pstVdecChn->mpi = NULL;
    }

    return TR_FAILURE;
}

HI_S32 HI_MPI_VDEC_DestroyChn(VDEC_CHN VdChn)
{
    int32_t ret;

    pthread_mutex_lock(&dec_mutex);

    MPP_VDEC_CHANNEL_S *pstVdecChn = &g_vdec_devices[0].pstChannels[VdChn];

    pthread_mutex_destroy(&pstVdecChn->mutex);

    if (pstVdecChn->packet)
    {
        mpp_packet_deinit(&pstVdecChn->packet);
        pstVdecChn->packet = NULL;
    }

    if (pstVdecChn->frm_grp)
    {
        mpp_buffer_group_put(&pstVdecChn->frm_grp);
        pstVdecChn->frm_grp = NULL;
    }

    if (pstVdecChn->mpi)
    {
        ret = pstVdecChn->mpi->reset(pstVdecChn->ctx);
        if (MPP_OK != ret)
        {
            LOG_ERR("VdChn %d, mpi->reset() failed.\n", VdChn);
        }
    }

    if (pstVdecChn->ctx)
    {
        mpp_destroy(pstVdecChn->ctx);
        pstVdecChn->ctx = NULL;
        pstVdecChn->mpi = NULL;
    }

    pstVdecChn->s32ChnId = -1;
    pstVdecChn->bStartRecv = FALSE;

    pthread_mutex_unlock(&dec_mutex);

    return TR_SUCCESS;
}

HI_S32 HI_MPI_VDEC_GetChnAttr(VDEC_CHN VdChn, VDEC_CHN_ATTR_S *pstAttr)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_StartRecvStream(VDEC_CHN VdChn)
{
    MPP_VDEC_CHANNEL_S *pstVdecChn = &g_vdec_devices[0].pstChannels[VdChn];

    pstVdecChn->bStartRecv = TRUE;

    return TR_SUCCESS;
}

HI_S32 HI_MPI_VDEC_StopRecvStream(VDEC_CHN VdChn)
{
    MPP_VDEC_CHANNEL_S *pstVdecChn = &g_vdec_devices[0].pstChannels[VdChn];

    pstVdecChn->bStartRecv = FALSE;

    return TR_SUCCESS;
}

HI_S32 HI_MPI_VDEC_Query(VDEC_CHN VdChn,VDEC_CHN_STAT_S *pstStat)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_GetFd(VDEC_CHN VdChn)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_CloseFd(VDEC_CHN VdChn)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_ResetChn(VDEC_CHN VdChn)
{
    MPP_VDEC_CHANNEL_S *pstVdecChn = &g_vdec_devices[0].pstChannels[VdChn];

    pstVdecChn->bStartRecv = FALSE;
    pstVdecChn->mpi->reset(pstVdecChn->ctx);
    pstVdecChn->bStartRecv = TRUE;

    return TR_SUCCESS;
}

HI_S32 HI_MPI_VDEC_SetChnParam(VDEC_CHN VdChn, VDEC_CHN_PARAM_S* pstParam)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_GetChnParam(VDEC_CHN VdChn, VDEC_CHN_PARAM_S* pstParam)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_SetProtocolParam(VDEC_CHN VdChn,VDEC_PRTCL_PARAM_S *pstParam)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_GetProtocolParam(VDEC_CHN VdChn,VDEC_PRTCL_PARAM_S *pstParam)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_SendStream(VDEC_CHN VdChn, const VDEC_STREAM_S *pstStream, HI_S32 s32MilliSec)
{
    int32_t ret;
    int pkt_done = 0;
    int times = 20;
    int32_t s32UsedTime = 0;

    MPP_VDEC_S *pstVdecDev = &g_vdec_devices[0];
    MPP_VDEC_CHANNEL_S *pstVdecChn = &g_vdec_devices[0].pstChannels[VdChn];

    if (!pstVdecChn->bStartRecv)
    {
        LOG_WAR("VdChn %d, is not start recv stream.\n", VdChn);
        return TR_FAILURE;
    }

    mpp_packet_set_data(pstVdecChn->packet, pstStream->pu8Addr);
    mpp_packet_set_pos(pstVdecChn->packet, pstStream->pu8Addr);
    mpp_packet_set_size(pstVdecChn->packet, pstStream->u32Len);
    mpp_packet_set_length(pstVdecChn->packet, pstStream->u32Len);

    if (pstStream->bEndOfFrame)
        mpp_packet_set_eos(pstVdecChn->packet);
    else
        mpp_packet_clr_eos(pstVdecChn->packet);

    mpp_packet_set_pts(pstVdecChn->packet, pstStream->u64PTS);
    mpp_packet_set_dts(pstVdecChn->packet, pstStream->u64PTS);

    do {
        ret = pstVdecChn->mpi->decode_put_packet(pstVdecChn->ctx, pstVdecChn->packet);
        if (MPP_OK == ret)
        {
            pkt_done = 1;
        }
        else
        {
            if (s32MilliSec == -1) //block
            {
                msleep(5);
            }
            else if (s32MilliSec > 0) //timeout
            {
                if (s32UsedTime >= s32MilliSec)
                    break;

                msleep(5);
                s32UsedTime += 5;
            }
        }
    } while (!pkt_done);

#if 1
    PIN_S *pPin = &pstVdecDev->stPinData.pstOutPins[VdChn];
    if (pkt_done && pPin->bConnect)
    {
        VIDEO_FRAME_INFO_S stFrameInfo = {0};

        ret = TR_MPI_VDEC_GetImage(VdChn, &stFrameInfo, -1);
        if (ret == TR_SUCCESS)
        {
            //pthread_mutex_lock(&pstVdecChn->mutex);

            if (pPin->stInfo.enModId == TR_ID_VPSS)
            {               
                TR_MPI_VPSS_SendFrame(pPin->stInfo.s32DevId,  &stFrameInfo, -1);
            }

            //pthread_mutex_unlock(&pstVdecChn->mutex);
        }

        //如果vpss内部有通道需要共享使用这个stFrameInfo,如何办？就不能在这release这个帧了
        TR_MPI_VDEC_ReleaseImage(VdChn, &stFrameInfo);
    }
#endif

    return pkt_done ? TR_SUCCESS : TR_FAILURE;
}

HI_S32 HI_MPI_VDEC_SendStreamEx(VDEC_CHN VdChn, const VDEC_STREAM_EX_S *pstStream, HI_S32 s32MilliSec)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_GetImage(VDEC_CHN VdChn, VIDEO_FRAME_INFO_S *pstFrameInfo,HI_S32 s32MilliSec)
{
    int32_t ret;
    int times = 20;
    int32_t s32UsedTime = 0;
    MppFrame mppframe = NULL;

    MPP_VDEC_CHANNEL_S *pstVdecChn = &g_vdec_devices[0].pstChannels[VdChn];
    
GET_AGAIN:
    ret = pstVdecChn->mpi->decode_get_frame(pstVdecChn->ctx, &mppframe);
    if (MPP_ERR_TIMEOUT == ret)
    {
        if (s32MilliSec == -1) //block
        {
            msleep(5);
            goto GET_AGAIN;
        }
        else if (s32MilliSec > 0) //timeout
        {
            if (s32UsedTime <= s32MilliSec)
            {
                msleep(5);
                s32UsedTime += 5;
                goto GET_AGAIN;
            }
        }
    }

    if (MPP_OK != ret || mppframe == NULL)
    {
        LOG_ERR("VdChn %d,  get frame failed, ret=%d\n", VdChn, ret);
        return TR_FAILURE;
    }

    if (mpp_frame_get_info_change(mppframe))
    {
        uint32_t width = mpp_frame_get_width(mppframe);
        uint32_t height = mpp_frame_get_height(mppframe);
        uint32_t hor_stride = mpp_frame_get_hor_stride(mppframe);
        uint32_t ver_stride = mpp_frame_get_ver_stride(mppframe);
        uint32_t buf_size = mpp_frame_get_buf_size(mppframe);

        LOG_INFO("VdChn %d, decode_get_frame get info changed found\n", VdChn);
        LOG_INFO("VdChn %d, decoder require buffer w:h [%d:%d] stride [%d:%d] buf_size %d\n",
                VdChn, width, height, hor_stride, ver_stride, buf_size);

        mpp_frame_deinit(&mppframe);
        mppframe = NULL;

        ret = pstVdecChn->mpi->control(pstVdecChn->ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
        if (MPP_OK != ret)
        {
            LOG_ERR("VdChn %d, info change ready failed.\n", VdChn);
        }

        //应该重新获取一次就可以获取到帧了吧？可以的，做过实验验证了。
        //goto GET_AGAIN;
    }
    else
    {
        uint32_t err_info = mpp_frame_get_errinfo(mppframe);
        uint32_t discard = mpp_frame_get_discard(mppframe);
        if (MPP_OK == err_info && MPP_OK == discard)
        {
            uint32_t width = mpp_frame_get_width(mppframe);
            uint32_t height = mpp_frame_get_height(mppframe);
            uint32_t hor_stride = mpp_frame_get_hor_stride(mppframe);
            uint32_t ver_stride = mpp_frame_get_ver_stride(mppframe);
            uint32_t buf_size = mpp_frame_get_buf_size(mppframe);

            pstFrameInfo->stVFrame.u32Width = mpp_frame_get_width(mppframe);
            pstFrameInfo->stVFrame.u32Height = mpp_frame_get_height(mppframe);
            pstFrameInfo->stVFrame.enPixelFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420;

            //fd ahb
            //uint64_t          u32PhyAddr[3];
            //void              *pVirAddr[3];
            //uint32_t          u32Stride[3];
            //pstFrameInfo->stVFrame.pHardwareBuf = fd2ahb(fd);

            pstFrameInfo->stVFrame.s16OffsetTop = 0;
            pstFrameInfo->stVFrame.s16OffsetBottom = pstFrameInfo->stVFrame.u32Height;
            pstFrameInfo->stVFrame.s16OffsetLeft = 0;
            pstFrameInfo->stVFrame.s16OffsetRight = pstFrameInfo->stVFrame.u32Width;

            pstFrameInfo->stVFrame.u64pts = mpp_frame_get_pts(mppframe);
            pstFrameInfo->stVFrame.u32TimeRef = 0;

            pstFrameInfo->stVFrame.u32PrivateData = (uint32_t)mppframe;
        }
        else
        {
            LOG_ERR("VdecChn %d, get frame err info: %d discard: %d\n", VdChn, err_info, discard);

            mpp_frame_deinit(&mppframe);
            mppframe = NULL;

            return TR_FAILURE;
        }
    }

    return TR_SUCCESS;
}

HI_S32 HI_MPI_VDEC_ReleaseImage(VDEC_CHN VdChn, VIDEO_FRAME_INFO_S *pstFrameInfo)
{
    MppFrame mppframe = (MppFrame)pstFrameInfo->stVFrame.u32PrivateData;
    //MPP_VDEC_CHANNEL_S *pstVdecChn = &g_vdec_devices[0].pstChannels[VdChn];

    if (mppframe)
    {
        mpp_frame_deinit(&mppframe);
        pstFrameInfo->stVFrame.u32PrivateData = 0;
    }

    return TR_SUCCESS;
}

HI_S32 HI_MPI_VDEC_GetUserData(VDEC_CHN VdChn, VDEC_USERDATA_S *pstUserData, HI_S32 s32MilliSec)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_ReleaseUserData(VDEC_CHN VdChn, VDEC_USERDATA_S *pstUserData)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_SetRotate(VDEC_CHN VdChn, ROTATE_E enRotate)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_GetRotate(VDEC_CHN VdChn, ROTATE_E *penRotate)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_GetChnLuma(VDEC_CHN VdChn, VDEC_CHN_LUM_S *pstLuma)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_SetUserPic(VDEC_CHN VdChn, VIDEO_FRAME_INFO_S *pstUsrPic)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_EnableUserPic(VDEC_CHN VdChn, HI_BOOL bInstant)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_DisableUserPic(VDEC_CHN VdChn)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_SetDisplayMode(VDEC_CHN VdChn, VIDEO_DISPLAY_MODE_E enDisplayMode)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_GetDisplayMode(VDEC_CHN VdChn, VIDEO_DISPLAY_MODE_E *penDisplayMode)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_SetChnVBCnt(VDEC_CHN VdChn, HI_U32 u32BlkCnt)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_GetChnVBCnt(VDEC_CHN VdChn, HI_U32 *pu32BlkCnt)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_AttachVbPool(VDEC_CHN VdChn, VDEC_CHN_POOL_S *pstPool)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_DetachVbPool(VDEC_CHN VdChn)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_SetModParam(VDEC_MOD_PARAM_S *pstModParam)
{
    return HI_SUCCESS;
}

HI_S32 HI_MPI_VDEC_GetModParam(VDEC_MOD_PARAM_S *pstModParam)
{
    return HI_SUCCESS;
}

